import datetime
from jili.core.printlog import print
import operator
from jili.core.convert import converttype
from talib import abstract
import pandas as pd
from jili.calc import windows,backwindows,forwardwindows
from jili.calc import base
from jili.calc import apply
# calcdf_set=base.getmodels("jili.calc.calcdf")
# function_graph_set=base.getmodels("jili.calc.function")
apply_set=base.getmodels("jili.calc.apply")
def getsyscalcor(calortype,name):
    if  calortype=="apply":
        if name in apply_set.keys():
            return apply_set[name]
        else:
            msg = "apply no exit name:" + name
            raise ValueError(msg)
    else:
        msg="no exit calortype:"+calortype
        raise ValueError(msg)
def version():
    a="V1.5.0"
    print(a)
    return a

class graphcalc():
    def __init__(self):
        self.onprogress=[]
        self.info={}
        self.__stop=False
    def setonprogress(self,oncall):
        if oncall not in self.onprogress:
            self.onprogress.append(oncall)
    def callonprogress(self,info={}):
        if self.onprogress:
            self.info["now"]=datetime.datetime.now()
            self.info.update(info)
            for i in self.onprogress:
                i(self.info)
    def run(self,grap):
        grap0=grap
        while True:
            startt=datetime.datetime.now()
            grap0.setonprogress(self.callonprogress)
            n=len(grap0.datatype)
            input=[]
            for i in grap0.input:
                if isinstance(i,operator):
                    for  oi in i.getoutput():
                        input.append(oi)
                else:
                    input.append(i)
            m=len(input)
            for i in range(0,m):
                if i<n:
                    input[i]=converttype(input[i],grap0.datatype[i])
            grap0.input=input
            grap0.run()
            endt=datetime.datetime.now()
            self.info["costtime"]=endt-startt
            self.callonprogress()
            if grap0.nextnode:
                grap0=grap0.nextnode
            else:
                break

class graphclass:
    def __init__(self):
        self.calcs={
            "ta":self.TA,
            "apply":self.apply,
            "function":self.function,
            "calcdf":self.calcdf,
            "foronbar":self.foronbar,
            "forwindows":self.forwindows
        }
    def setcalcors(self,calcors):
        self.calcors = calcors
    def run(self,k,calcors):
        out=k
        for i in calcors:
            calc_cmd=i["calc_cmd"]
            try:
                out=self.calcs[calc_cmd](i,out)
            except Exception as e:
                print(e,i)
        return out
    def TA(self,ta,k):
        fun = abstract.Function(ta["cmd"])
        if "arg" in ta.keys():
            arg = ta["arg"]
            fun.set_parameters(arg)
        if "input" in ta.keys():
            input = ta["input"]
            fun.set_input_names(input)
        input_names = []
        for i in input.values():
            if isinstance(i,str):
                if i not in input_names:
                    input_names.append(i)
            else:
                for j in i:
                    if j not in input_names:
                        input_names.append(j)
        #print(input_names)
        if len(k[input_names].dropna()) > 0:
            if fun.set_input_arrays(k):
                out_names = ta["out"]
                if len(out_names) >= 2:
                    n = 0
                    for i in out_names:
                        nk = fun.output_names[n]
                        k[i] = fun.outputs[nk]
                        n = n + 1
                else:
                    i = out_names[0]
                    k[i] = fun.outputs
            return k
        else:
            if fun.set_input_arrays(k):
                out_names = ta["out"]
                k[out_names[0]] = pd.nan
            return k
    def apply(self,ta,k):
        o=ta["out"]
        fun=ta["apply"]
        if isinstance(fun,str):
            deal=getsyscalcor("apply",fun)
        else:
            deal=fun
        axis0=int(ta.get("axis",1))
        if "arg" in ta.keys():
            arg = ta.get("arg",())
            k[o]=k.apply(deal,axis=axis0,args=arg)
        return k
    def calcdf(self,ta,k):
        fun=ta["calcdf"]
        if isinstance(fun,str):
            deal=getsyscalcor("calcdf",fun)
        else:
            deal=fun
        arg = ta.get("arg",{})
        outnames=ta.get("output","out")
        outputs=deal(k,ta["arg"])
        if isinstance(outputs,list):
            for i in range(len(outputs)):
                key=outnames[i]
                k[key] = outputs[i]
        elif isinstance(outputs,pd.Series):
            key = outnames[0]
            k[key] = outputs
        elif isinstance(outputs,tuple):
            for i in range(len(outputs)):
                key = outnames[i]
                k[key] = outputs[i]
        return k
    def function(self,ta,k):
        fun = ta["function"]
        if isinstance(fun, str):
            deal = getsyscalcor("calcdf", fun)
        else:
            deal = fun
        return deal(k,ta["arg"])
    def foronbar(self,ta,k):
        if isinstance(k,pd.DataFrame):
            k=k.to_dict("records")
        for t in ta["tasks"]:
            if "arg" in t.keys():
                t["object"].setarg(t["arg"])
        for i in k:
            for t in ta["tasks"]:
                out=t["object"].onbar(i)
                if isinstance(t["out"],list):
                    n=0
                    for o in t["out"]:
                        i[o]=out[n]
                        n=n+1
                else:
                    o=t["out"]
                    i[o] = out
        return k
    def forwindows(self,ta,k):
        if isinstance(k,pd.DataFrame):
            k=k.to_dict("records")
        for t in ta["tasks"]:
            if "arg" in t.keys():
                t["object"].setarg(t["arg"])
        batch=ta["batch"]
        fx1=0
        if "fx" in ta.keys():
            fx1=int(ta["fx"])
        if fx1==0:
            kl=backwindows(k,batch)
        else:
            kl=forwardwindows(k,batch)
        for i in kl:
            for t in ta["tasks"]:
                out=t["object"].onwindows(i)
                if fx1 == 0:
                    if isinstance(t["out"],list):
                        n=0
                        for o in t["out"]:
                            i[-1][o]=out[n]
                            n=n+1
                    else:
                        o=t["out"]
                        i[-1][o] = out
                else:
                    if isinstance(t["out"],list):
                        n=0
                        for o in t["out"]:
                            i[0][o]=out[n]
                            n=n+1
                    else:
                        o=t["out"]
                        i[0][o] = out
        return k


graph=graphclass()
class onbar:
    def __init__(self):
        self.a="close"
        self.b="high"
        self.batch=2
        self.pre=None
    def setarg(self,arg):
        self.a=arg.get("a","close")
        self.b = arg.get("b", "high")
        self.batch = arg.get("batch", 2)
        self.pre = None
    def onbar(self,bar):
        rst=None
        if self.pre:
            rst= bar[self.b]-self.pre
            self.pre=bar[self.a]
        else:
            self.pre = bar[self.a]
        return rst
    def onwindows(self,bars):
        rst=[None,None]
        if len(bars)==self.batch:
            rst[0]=bars[-1][self.b]-bars[0][self.a]
            rst[1] = -bars[-1]["low"] + bars[0][self.a]
        return rst

if __name__=="__main__":
    from jili.data.db import getk1mbar_pkl
    k = getk1mbar_pkl("TA101")
    onbar1=onbar()
    #import pandas as pd
    #kd = pd.DataFrame(k)
    k0=k[:10]
    c=[
        {'calc_cmd': 'foronbar', 'tasks':[{ 'object': onbar1, 'arg': {'a':'close','b':'high'},'out':'XXX'}]},
        {'calc_cmd': 'forwindows', 'batch':2,'tasks': [{'object': onbar1, 'arg': {'a': 'close', 'b': 'high'}, 'out': ['X',"XX"]}]},
    ]
    k0=graph.run(k0,c)
    for i in k0:
        print(i)




